AMD IOMMU: Defer IO pagetable construction until device assignment
authorKeir Fraser <keir.fraser@citrix.com>
Fri, 4 Apr 2008 12:54:05 +0000 (13:54 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Fri, 4 Apr 2008 12:54:05 +0000 (13:54 +0100)
During HVM domain creation, I/O page tables are filled by coping p2m
entries from p2m table, which is a useless step for non-passthru
domain. This patch defers I/O page table construction until the moment
of device assignment. In case that pci devices are never assigned or
hot plugged, the unnecessary duplication will be avoided.

Signed-off-by: Wei Wang <wei.wang2@amd.com>
xen/drivers/passthrough/amd/iommu_map.c
xen/drivers/passthrough/amd/pci_amd_iommu.c
xen/include/asm-x86/hvm/svm/amd-iommu-proto.h
xen/include/xen/hvm/iommu.h

index ad5a8517bd5438e51c4dade46ddb624d6e72f4c1..24ff5eacb892e763c763dd0a8c9bc229a15e36fa 100644 (file)
@@ -388,17 +388,17 @@ int amd_iommu_map_page(struct domain *d, unsigned long gfn, unsigned long mfn)
     unsigned long flags;
     u64 maddr;
     struct hvm_iommu *hd = domain_hvm_iommu(d);
-    int iw, ir;
+    int iw = IOMMU_IO_WRITE_ENABLED;
+    int ir = IOMMU_IO_READ_ENABLED;
 
     BUG_ON( !hd->root_table );
 
-    maddr = (u64)mfn << PAGE_SHIFT;
-
-    iw = IOMMU_IO_WRITE_ENABLED;
-    ir = IOMMU_IO_READ_ENABLED;
-
     spin_lock_irqsave(&hd->mapping_lock, flags);
 
+    if ( is_hvm_domain(d) && !hd->p2m_synchronized )
+        goto out;
+
+    maddr = (u64)mfn << PAGE_SHIFT;
     pte = get_pte_from_page_tables(hd->root_table, hd->paging_mode, gfn);
     if ( pte == NULL )
     {
@@ -409,7 +409,7 @@ int amd_iommu_map_page(struct domain *d, unsigned long gfn, unsigned long mfn)
     }
 
     set_page_table_entry_present((u32 *)pte, maddr, iw, ir);
-
+out:
     spin_unlock_irqrestore(&hd->mapping_lock, flags);
     return 0;
 }
@@ -425,11 +425,17 @@ int amd_iommu_unmap_page(struct domain *d, unsigned long gfn)
 
     BUG_ON( !hd->root_table );
 
+    spin_lock_irqsave(&hd->mapping_lock, flags);
+
+    if ( is_hvm_domain(d) && !hd->p2m_synchronized )
+    {
+        spin_unlock_irqrestore(&hd->mapping_lock, flags);
+        return 0;
+    }
+
     requestor_id = hd->domain_id;
     io_addr = (u64)gfn << PAGE_SHIFT;
 
-    spin_lock_irqsave(&hd->mapping_lock, flags);
-
     pte = get_pte_from_page_tables(hd->root_table, hd->paging_mode, gfn);
     if ( pte == NULL )
     {
@@ -486,3 +492,53 @@ int amd_iommu_reserve_domain_unity_map(
     spin_unlock_irqrestore(&hd->mapping_lock, flags);
     return 0;
 }
+
+int amd_iommu_sync_p2m(struct domain *d)
+{
+    unsigned long mfn, gfn, flags;
+    void *pte;
+    u64 maddr;
+    struct list_head *entry;
+    struct page_info *page;
+    struct hvm_iommu *hd;
+    int iw = IOMMU_IO_WRITE_ENABLED;
+    int ir = IOMMU_IO_READ_ENABLED;
+
+    if ( !is_hvm_domain(d) )
+        return;
+
+    hd = domain_hvm_iommu(d);
+
+    spin_lock_irqsave(&hd->mapping_lock, flags);
+
+    if ( hd->p2m_synchronized )
+        goto out;
+
+    for ( entry = d->page_list.next; entry != &d->page_list;
+            entry = entry->next )
+    {
+        page = list_entry(entry, struct page_info, list);
+        mfn = page_to_mfn(page);
+        gfn = get_gpfn_from_mfn(mfn);
+
+        if ( gfn == INVALID_M2P_ENTRY )
+            continue;
+
+        maddr = (u64)mfn << PAGE_SHIFT;
+        pte = get_pte_from_page_tables(hd->root_table, hd->paging_mode, gfn);
+        if ( pte == NULL )
+        {
+            dprintk(XENLOG_ERR,
+                    "AMD IOMMU: Invalid IO pagetable entry gfn = %lx\n", gfn);
+            spin_unlock_irqrestore(&hd->mapping_lock, flags);
+            return -EFAULT;
+        }
+        set_page_table_entry_present((u32 *)pte, maddr, iw, ir);
+    }
+
+    hd->p2m_synchronized = 1;
+
+out:
+    spin_unlock_irqrestore(&hd->mapping_lock, flags);
+    return 0;
+}
index 8b93e9c3cde63c6e69ed32f1dbb7ea2185b30068..4f562757f790002ff1e6db4df0eb07c3d90944d2 100644 (file)
@@ -553,8 +553,9 @@ static int reassign_device( struct domain *source, struct domain *target,
 int amd_iommu_assign_device(struct domain *d, u8 bus, u8 devfn)
 {
     int bdf = (bus << 8) | devfn;
-    int req_id;
-    req_id = ivrs_mappings[bdf].dte_requestor_id;
+    int req_id = ivrs_mappings[bdf].dte_requestor_id;
+
+    amd_iommu_sync_p2m(d);
 
     if ( ivrs_mappings[req_id].unity_map_enable )
     {
index 8899f27a73da04ccbb2cb2a42256f8d0541699d0..d64913ce58cf1634401d4fde0b13bf00abebd7d3 100644 (file)
@@ -57,6 +57,7 @@ int amd_iommu_unmap_page(struct domain *d, unsigned long gfn);
 void *amd_iommu_get_vptr_from_page_table_entry(u32 *entry);
 int amd_iommu_reserve_domain_unity_map(struct domain *domain,
         unsigned long phys_addr, unsigned long size, int iw, int ir);
+int amd_iommu_sync_p2m(struct domain *d);
 
 /* device table functions */
 void amd_iommu_set_dev_table_entry(u32 *dte, u64 root_ptr,
index 8b11d44c167bd7c45244e3a852aa0867bb61454b..f9d0de5a0d3b054c0425fe067c314c23962760c8 100644 (file)
@@ -48,9 +48,10 @@ struct hvm_iommu {
     int domain_id;
     int paging_mode;
     void *root_table;
+    bool_t p2m_synchronized;
 
     /* iommu_ops */
     struct iommu_ops *platform_ops;
 };
 
-#endif // __ASM_X86_HVM_IOMMU_H__
+#endif /* __ASM_X86_HVM_IOMMU_H__ */